#include "StdAfx.h"
#include "ConsoleEmbWnd.h"



ConsoleEmbWnd::ConsoleEmbWnd()
{
    m_hWnd=NULL;
    m_hOut=NULL;
    m_hIn=NULL;
	m_hCmd=NULL;
    m_fontSize.X=0;
    m_fontSize.Y=0;
};

BOOL ConsoleEmbWnd::CanCreate()
{
    if(NULL==GetConsoleWindow())
    {
        return TRUE;
    }
    return FALSE;
}

BOOL ConsoleEmbWnd::Create(fnCosolCB cbFn, void* cbCtx)
{
    if( (NULL!=m_hWnd)
        || (NULL!=GetConsoleWindow()) )
    {
        return FALSE;
    }

    if(FALSE==AllocConsole())
    {
        return FALSE;
    }

	TCHAR szCmdLine[MAX_PATH]={TEXT("cmd.exe")};
	STARTUPINFO si={0};
	si.cb=sizeof(si);
	PROCESS_INFORMATION pi;
    BOOL ret=CreateProcess(NULL, szCmdLine,
            		NULL, NULL,
            		FALSE, 0,
            		NULL, NULL,
            		&si, &pi );
    if(FALSE==ret)
    {
        FreeConsole();
        return FALSE;
    }
	CloseHandle(pi.hThread);
	m_hCmd=pi.hProcess;

    m_fnCosolCB=cbFn;
    m_cbCtx=cbCtx;

    m_hThrd=CreateThread(NULL, 0, ConsoleEmbWnd::ConSoleThrdFn, this, 0, NULL);
    m_evntWndClose=CreateEvent(NULL, FALSE, FALSE, NULL);

    m_hWnd=GetConsoleWindow();
    m_hOut=GetStdHandle( STD_OUTPUT_HANDLE );
	m_hIn=GetStdHandle( STD_INPUT_HANDLE );
    CONSOLE_FONT_INFO cfi;
    GetCurrentConsoleFont( m_hOut, FALSE, &cfi);
    m_fontSize = GetConsoleFontSize( m_hOut, cfi.nFont);

	CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo( m_hOut, &csbi );
	if(csbi.dwSize.Y<1000)
	{
		// set console buffer deep enough
		csbi.dwSize.Y=1000;
		SetConsoleScreenBufferSize(m_hOut, csbi.dwSize);
	}

	DWORD dwLen;
	for(int i=0;;i++)
	{
		dwLen=GetPromoteStr(m_lastPath, sizeof(m_lastPath)/sizeof(m_lastPath[0]));
		if(	0==dwLen || dwLen>sizeof(m_lastPath)/sizeof(m_lastPath[0]))
		{
			Sleep(10);
		}
		else
		{
			break;
		}
	};

	return TRUE;
}
VOID ConsoleEmbWnd::Delete()
{
    if(NULL!=m_hOut)
    {
        SetEvent(m_evntWndClose);
        WaitForSingleObject(m_hThrd, 500);
        CloseHandle( m_hThrd );
        CloseHandle( m_evntWndClose );

        CloseHandle( m_hOut );
        CloseHandle( m_hIn );
//        CloseHandle( m_hWnd );
        FreeConsole();

		if(NULL!=m_hCmd)
		{
			TerminateProcess(m_hCmd, 0);

		}

		m_hWnd=NULL;
		m_hOut=NULL;
		m_hIn=NULL;
		m_hCmd=NULL;
    }
};

BOOL ConsoleEmbWnd::MoveWindow(int X, int Y, int nWidth, int nHeight, BOOL bRepaint)
{
	BOOL fRet;

	CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo( m_hOut, &csbi );

	int cxb=GetSystemMetrics(SM_CXHSCROLL);
    
	nWidth=(nWidth-cxb+m_fontSize.X-1)/m_fontSize.X;
    nHeight=(nHeight+m_fontSize.Y-1)/m_fontSize.Y;
	if( 0>=nWidth
		|| 0==nHeight )
	{
		nWidth=csbi.dwSize.X;
		nHeight=csbi.srWindow.Bottom-csbi.srWindow.Top+1;
	}

    // set the buffer
	if(nWidth!=csbi.dwSize.X)
	{
		csbi.dwSize.X=nWidth;
	}
	csbi.srWindow.Bottom=csbi.srWindow.Top+nHeight-1;

	if(csbi.dwSize.X>=csbi.srWindow.Right)
	{
		fRet=SetConsoleScreenBufferSize(m_hOut, csbi.dwSize);
		csbi.srWindow.Right=nWidth-1;
		fRet+=SetConsoleWindowInfo(m_hOut, TRUE, &csbi.srWindow);
	}
	else
	{
		csbi.srWindow.Right=nWidth-1;
		fRet=SetConsoleWindowInfo(m_hOut, TRUE, &csbi.srWindow);
		fRet=SetConsoleScreenBufferSize(m_hOut, csbi.dwSize);
	}

    WINDOWINFO wi;
    wi.cbSize=sizeof(wi);
    GetWindowInfo(m_hWnd, &wi);
    fRet=::MoveWindow(m_hWnd
				, X-(wi.rcClient.left-wi.rcWindow.left), Y-(wi.rcClient.top-wi.rcWindow.top)
                , wi.rcWindow.right-wi.rcWindow.left
				, wi.rcWindow.bottom-wi.rcWindow.top
				, bRepaint);
	return fRet;
}

DWORD ConsoleEmbWnd::GetPromoteStr(TCHAR* szProm, DWORD dwCnt)
{
	BOOL fRet;

	CONSOLE_SCREEN_BUFFER_INFO csbi;
    fRet=GetConsoleScreenBufferInfo( m_hOut, &csbi );
	if(0==csbi.dwCursorPosition.X)
	{
		return 0;
	}

    DWORD dwLine=(MAX_PATH+csbi.dwSize.X-1)/csbi.dwSize.X;
    if(dwLine>(DWORD)csbi.dwCursorPosition.Y)
    {
        dwLine=csbi.dwCursorPosition.Y;
    }
    DWORD dwStartLine=dwLine-1;

	CHAR_INFO ci[MAX_PATH*2];
	COORD bs;
	COORD bc={0, 0};
	SMALL_RECT rr;

    // read the last line
    bs.X=csbi.dwSize.X;
    bs.Y=1;
	rr.Left=0;
	rr.Right=csbi.dwCursorPosition.X-1;
	rr.Top=csbi.dwCursorPosition.Y;
	rr.Bottom=rr.Top;
	fRet=ReadConsoleOutput( m_hOut, &ci[csbi.dwSize.X*dwStartLine], bs, bc, &rr);
    // check the last prompt charactor
    if('>'!=ci[csbi.dwSize.X*dwStartLine+csbi.dwCursorPosition.X-1].Char.UnicodeChar)
    {
        return 0;
    }
    ci[csbi.dwSize.X*dwStartLine+csbi.dwCursorPosition.X-1].Char.UnicodeChar=0;

    // check prompt start
    while( (':'!=ci[csbi.dwSize.X*dwStartLine+1].Char.UnicodeChar)
            || ('A'>ci[csbi.dwSize.X*dwStartLine].Char.UnicodeChar)
            || ('Z'<ci[csbi.dwSize.X*dwStartLine].Char.UnicodeChar) )
    {
        if(0==dwStartLine)
        {
            // not find
            return 0;
        }
        dwStartLine--;

        bs.X=csbi.dwSize.X;
        bs.Y=1;
        bc.X=0;
		bc.Y=0;
        rr.Left=0;
        rr.Right=csbi.dwSize.X-1;
        rr.Top=(SHORT)(csbi.dwCursorPosition.Y-dwLine+dwStartLine+1);
        rr.Bottom=rr.Top;
        fRet=ReadConsoleOutput( m_hOut, &ci[csbi.dwSize.X*dwStartLine], bs, bc, &rr);
    }

    if(dwCnt<(dwLine-dwStartLine-1)*csbi.dwSize.X+csbi.dwCursorPosition.X)
    {
        return (dwLine-dwStartLine-1)*csbi.dwSize.X+csbi.dwCursorPosition.X;
    }
    dwCnt=(dwLine-dwStartLine-1)*csbi.dwSize.X+csbi.dwCursorPosition.X-1;

    for(DWORD i=0; i<=dwCnt; i++)	
    {
#ifdef UNICODE
        szProm[i]=ci[dwStartLine*csbi.dwSize.X+i].Char.UnicodeChar;
#else
        szProm[i]=ci[dwStartLine*csbi.dwSize.X+i].Char.AsciiChar;
#endif
    }

    return dwCnt;
}

VOID ConsoleEmbWnd::OutputChar(TCHAR c)
{
	BOOL fRet;

	INPUT_RECORD r;
	r.EventType=KEY_EVENT;
	r.Event.KeyEvent.bKeyDown = TRUE;
	r.Event.KeyEvent.wRepeatCount = 1;
	r.Event.KeyEvent.wVirtualKeyCode = 0;
	r.Event.KeyEvent.wVirtualScanCode = 0;
#ifdef UNICODE
	r.Event.KeyEvent.uChar.UnicodeChar = c;
#else
	r.Event.KeyEvent.uChar.AsciiChar = c;
#endif
	r.Event.KeyEvent.dwControlKeyState = 0;

	DWORD dwWriten;

	fRet=WriteConsoleInput( m_hIn , &r, 1, &dwWriten);//key down event
	dwWriten=GetLastError();

	r.Event.KeyEvent.bKeyDown = FALSE;
	fRet=WriteConsoleInput( m_hIn , &r, 1, &dwWriten);//key up
}

VOID ConsoleEmbWnd::OutputStr(const TCHAR* str)
{
	for(int i=0; 0!=str[i]; i++)
	{
		OutputChar(str[i]);
	}
}

BOOL ConsoleEmbWnd::ChangDir(const TCHAR* szDir)
{
	TCHAR szPromt[MAX_PATH];
	DWORD dwLen=GetPromoteStr(szPromt, sizeof(szPromt)/sizeof(szPromt[0]));
	if(	0==dwLen || dwLen>sizeof(szPromt)/sizeof(szPromt[0]))
	{
		// not in prompt stat
		return FALSE;
	}
    if((FILE_ATTRIBUTE_DIRECTORY&GetFileAttributes(szDir))==0)
    {
        return FALSE;
    }
    _tcscpy_s(m_lastPath, sizeof(m_lastPath)/sizeof(m_lastPath[0]), szDir);

	szPromt[dwLen-1]=0;
	if(0!=_tcsicmp(szDir, szPromt))
	{
		// change dir
		OutputStr(TEXT("cd "));
		OutputStr(szDir);
		OutputStr(TEXT("\r\n"));

		if(_toupper(szPromt[0])!=_toupper(szDir[0]))
		{
			// change disk
			TCHAR cd[]={TEXT("C:\r\n")};
			cd[0]=szDir[0];
			OutputStr(cd);
		}
	}

	return TRUE;
}

BOOL ConsoleEmbWnd::GetDir(TCHAR* szDir)
{
	TCHAR szPromt[MAX_PATH];
	DWORD dwLen=GetPromoteStr(szPromt, sizeof(szPromt)/sizeof(szPromt[0]));
	if(	0==dwLen || dwLen>sizeof(szPromt)/sizeof(szPromt[0]))
	{
		// not in prompt stat
		return FALSE;
	}
    _tcscpy_s(m_lastPath, sizeof(m_lastPath)/sizeof(m_lastPath[0]), szPromt);
    _tcscpy_s(szDir, sizeof(szPromt)/sizeof(szPromt[0]), szPromt);

	return TRUE;
}

DWORD ConsoleEmbWnd::ConSoleThrdFn(LPVOID pthis)
{
    return ((ConsoleEmbWnd*)pthis)->ConSoleThrd();
}

DWORD ConsoleEmbWnd::ConSoleThrd()
{
    HANDLE hWait[]={m_hCmd, m_evntWndClose};

    while(1)
    {
        DWORD dwWaitRet=WaitForMultipleObjects(sizeof(hWait)/sizeof(hWait[0]), hWait, FALSE, 1000);
        if( (WAIT_OBJECT_0+0)==dwWaitRet )
        {
            if(NULL!=m_fnCosolCB)
            {
                m_fnCosolCB(m_cbCtx, CNSL_WND_CB_CMD_EXIT, NULL);
            }
            CloseHandle(m_hCmd);
            m_hCmd=NULL;
            break;
        }
        else if( (WAIT_OBJECT_0+1)==dwWaitRet )
        {
            break;
        }
        else if( WAIT_TIMEOUT==dwWaitRet )
        {
            TCHAR szPromt[MAX_PATH];
            DWORD dwLen=GetPromoteStr(szPromt, sizeof(szPromt)/sizeof(szPromt[0]));
            if( 0==dwLen || dwLen>sizeof(szPromt)/sizeof(szPromt[0]))
            {
                // not in prompt stat
                continue;
            }
            if(0!=_tcsicmp(m_lastPath, szPromt))
            {
                _tcscpy_s(m_lastPath, sizeof(m_lastPath)/sizeof(m_lastPath[0]), szPromt);
                if(NULL!=m_fnCosolCB)
                {
                    m_fnCosolCB(m_cbCtx, CNSL_WND_CB_CD_DIR, szPromt);
                }
            }
        }
    }

    return 0;
    
}

